import {Meta} from '@storybook/addon-docs/blocks';

<Meta title="Composables/useFetch" />

# useFetch

`useFetch` is designed for interactions with our API.

## Technical decisions

The promise-based [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) serves as a modern replacement for [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest). It is already sufficiently supported across various browser versions, making it a reliable choice.

There are several libraries available to enhance the Fetch API and add useful features. We have chosen to use the [ofetch](https://github.com/unjs/ofetch), a fundamental component of the well-known Vue.js framework [Nuxt.js](https://nuxt.com), as the underlying library. We believe this provides a good chance of long-term support.

## Features

This is not the ultimate list of features, but highlights the ones that are relevant to our usage.

- **Auto Retry** implemented in [ofetch](https://github.com/unjs/ofetch#%EF%B8%8F-auto-retry) and helps with unstable internet connections. Currently it retries only for GET requests.
- **Encoding Query params** implemented in [ofetch](https://github.com/unjs/ofetch#%EF%B8%8F-adding-query-search-params) to make it easy to pass query params that are automatically correctly encoded.
- **CSRF token** implemented in **useFetch** to automatically add CSRF tokens in headers for POST/PUT/DELETE requests.
- **Last request wins** implemented in **useFetch**. Typical use case is if the user is changing filters/search criteria on slower connection to make sure that the last request data are applied. Internally it aborts previous requests.
- **Automatic X-Http-Method-Override** implemented in **useFetch**. For better [compatibility](https://github.com/pkp/pkp-lib/issues/5981) with some servers, we use **X-Http-Method-Override** header for **PUT/DELETE** requests, while keeping method as **POST**.
- **Error Handling** is implemented in useFetch. By default, any response that is not a **200** status is considered an unexpected error, and a dialog is displayed to inform the user. If you anticipate a validation error of user input (**4xx** status codes) that require handling, this must be explicitly enabled. For more details, refer to the [POST 4xx](?path=/docs/composables-usefetch--docs#post-4xx-with-validation-error) in the Examples section.
- **Debounce** when request should be debounced, useFetch supports `debounceMs` option.

## API

For a detailed list of options please check `useFetch.js`. For common use cases the following example should provide enough guidance.

## Examples

Note that currently API URLs are generated on the PHP side and passed to the pages on initial load. In future we intend to bring such utilities to the client side as well.

All examples can be found also in `useFetch.test.js`. There might be use cases that we have not covered yet; feel free to open a [github issue](https://github.com/pkp/pkp-lib/issues) to bring these to our attention.

### GET 200

```javascript
const url = ref(submissionsGetApiUrl);
const queryParams = ref({param1: 4, param2: 5});
// url, query and body can be normal objects or Refs
const {data, isLoading, fetch} = useFetch(url, {
	query: queryParams,
});

console.log(isLoading.value); // false
console.log(data.value); // null

await fetch();
console.log(data.value); // { ... } data returned from server
console.log(isLoading.value); // false
```

### POST 200

```javascript
const body = {title: 'abc'};
const url = ref(submissionPostApiUrl);

// url, query and body can be normal objects or Refs
const {data, iSuccess, validationError, fetch} = useFetch(url, {
	method: 'POST',
	body,
	// important if we expect endpoint to validate user input
	// if endpoint returns 4xx, response is saved to validationError
	expectValidationError: true,
});

await fetch();

console.log(validationError.value); // null
console.log(data.value); // {...} data returned from server
console.log(data.isSuccess); // true
```

### POST 4xx with validation error

```javascript
const body = {title: 'abc'};
const url = ref(submissionPostApiUrl);

// url, query and body can be normal objects or Refs
const {data, validationError, fetch} = useFetch(url, {
	method: 'POST',
	body,
	// important if we expect endpoint to validate user input
	// if endpoint returns 4xx, response is saved to validationError
	expectValidationError: true,
});

await fetch();

console.log(validationError.value); // {title: ['Unsupported characters']}
console.log(data.value); // null
```

### POST 500

```javascript
const body = {title: 'abc'};
const url = ref(submissionPostApiUrl);

// url, query and body can be normal objects or Refs
const {data, isSuccess, fetch} = useFetch(url, {
	method: 'POST',
	body,
});

await fetch();

// Useful when some follow-up action should be triggered based on successfull request
console.log(isSuccess.value); // false
console.log(data.value); // null
```

### PUT 200

```javascript
const url = ref(submissionPutApiUrl);
const {data, validationError, fetch} = useFetch(url, {
	method: 'PUT',
	body: {title: 'abc'},
	expectValidationError: true,
});

await fetch();

console.log(validationError.value); // null
console.log(data.value); // {...} data returned from server
```
